package sun.scheduler.util.graph;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;


/**
 * Represents a GraphNode.
 * @param <N>
 * @author mfj
 */
public class Node<N, E> implements Iterable<Node<N, E>>
{

    /**
     * The unique identifyer of this node.
     */
    protected final int id;
    /**
     * The data content.
     */
    protected N data;
    /**
     * A List of all outgoing edges.
     */
    public ArrayList<Edge<N, E>> outEdges = new ArrayList<Edge<N, E>>();
    
    /**
     * A List of all incomming edges.
     */
    public ArrayList<Edge<N, E>> inEdges = new ArrayList<Edge<N, E>>();
    
    /**
     * The graph of the node.
     */
    protected Graph<N, E> graph;

    /**
     * If the node is in an Topologicaly orderd Graph this is hit order number
     */
    protected int OrderID;

    public int getOrderID() {
        return OrderID;
    }

    public void setOrderID(int OrderID) {
        this.OrderID = OrderID;
    }

    /**
     * Instantiates a new <code>Node</code>.
     * @param id the id of this node.
     * @param data the data content of this node.
     * @param graph
     */
    public Node(int id, N data, Graph<N, E> graph)
    {
        this.id = id;
        this.data = data;
        this.graph = graph;
    }


    /**
     * Gets the id of this node.
     * @return the id.
     */
    public int id()
    {
        return id;
    }

    /**
     * Returns the number of outgoing edges.
     * @return number of outgoing edges
     */
    public int getNumOfOutgoingEdges()
    {
        return outEdges.size();
    }
    
    /**
     * Gets the edge that is stored at the specified index.
     * @param k the index where the edge is stored.
     */
    public Edge<N,E> getOutgoingEdge(int k)
    {
        return outEdges.get(k);
    }
    
    /**
     * Gets the data of this node.
     * @return the data.
     */
    public N getData()
    {
        return data;
    }


    /**
     * Sets the data of this node.
     * @param data the new data of this node.
     */
    public void setData(N data)
    {
        this.data = data;
    }

    /**
     * Gets the {@see Graph} in which this node is contained.
     * @return the graph of the node.
     */
    public Graph<N, E> getGraph()
    {
        return graph;
    }


    public ArrayList<Edge<N, E>> getInEdges()
    {
        return inEdges;
    }


    public ArrayList<Edge<N, E>> getOutEdges()
    {
        return outEdges;
    }


    /**
     * Gets an iterator of all nodes reachable by some outgoing edge.
     * @return
     */
    @Override
    public Iterator<Node<N, E>> iterator()
    {
        final Iterator<Edge<N, E>> iter = edgeIterator();
        return new Iterator<Node<N, E>>()
        {

            Node<N, E> curr = null;


            @Override
            public boolean hasNext()
            {
                return iter.hasNext();
            }


            @Override
            public Node<N, E> next()
            {
                return curr = iter.next().end;
            }


            @Override
            public void remove()
            {
                throw new UnsupportedOperationException("Not supported.");
            }
        };
    }

    /**
     * Gets an iterator that traverses over all outgoing edges.
     * @return
     */
    public Iterator<Edge<N, E>> edgeIterator()
    {
        return outEdges.iterator();
    }

    /**
     * Adds a new outgoing edge.
     * @param edge
     */
    protected void addOutgoingEdge(Edge<N, E> edge)
    {
        outEdges.add(edge);
    }


    /**
     * Removes an outgoing edge.
     * @param edge
     */
    protected void removeOutgoingEdge(Edge edge)
    {
        outEdges.remove(edge);
    }
    
    /**
     * Adds a new incoming edge.
     * @param edge
     */
    protected void addIncomingEdge(Edge<N, E> edge)
    {
        inEdges.add(edge);
    }


    /**
     * Removes an incoming edge.
     * @param edge
     */
    protected void removeIncomingEdge(Edge edge)
    {
        inEdges.remove(edge);
    }


    /**
     *
     * @return
     */
    @Override
    public String toString()
    {
        return "(" + "id=" + id + ", data=" + data + ')';
    }


    /**
     * Compares this node to some object.
     * @param obj the object to compare to.
     * @return <code>true</code> if the refferences are equal,
     * <code>false</code> otherwise.
     */
    @Override
    public boolean equals(Object obj)
    {
        return this == obj;
    }


    /**
     *
     * @return
     */
    @Override
    public int hashCode()
    {
        return id;
    }
}
